#include "screenbuffer.h"
#include "getwindow.h"
#include "logging.hpp"
#include <cstdio>
#include <cstring>

#ifdef _WIN32

ScreenBuffer_t::ScreenBuffer_t()
    : MAXCOLS(500), MAXROWS(200), _WindowSize({MAXROWS, MAXCOLS}), FullScreenSize({0, 0}), drawer(this), updater(this),
      storage(new char[(MAXROWS * MAXCOLS) + 1])
{
    this->GetWindowSize();
}
ScreenBuffer_t ::~ScreenBuffer_t()
{
    delete[] storage;
}

Action_t GetWindowSize(Coord_t& coord)
{
    CONSOLE_SCREEN_BUFFER_INFO csbi;

    if (!GetConsoleScreenBufferInfo(GetStdHandle(STD_OUTPUT_HANDLE), &csbi))
    {
        coord.Y = -1;
        coord.X = -1;
        DWORD errorCode = GetLastError();
        printf("GetConsoleScreenBufferInfo failed with code: %lu\n", errorCode);
        return -1;
    }
    coord.Y = csbi.srWindow.Bottom - csbi.srWindow.Top + 1;
    coord.X = csbi.srWindow.Right - csbi.srWindow.Left + 1;
#ifdef GETSIZE_FALLBACK
    if (coord.Y <= 0 || coord.X <= 0) GetWindowSize(coord);
#endif
    return 0;
}
Action_t ScreenBuffer_t::GetWindowSize(Coord_t& coord)
{
    CONSOLE_SCREEN_BUFFER_INFO csbi;

    GetConsoleScreenBufferInfo(GetStdHandle(STD_OUTPUT_HANDLE), &csbi);
    coord.Y = csbi.srWindow.Bottom - csbi.srWindow.Top + 1;
    coord.X = csbi.srWindow.Right - csbi.srWindow.Left + 1;
#ifdef GETSIZE_FALLBACK
    if (coord.Y <= 0 || coord.X <= 0) GetWindowSize(coord);
#endif
    FullScreenSize = {short(_WindowSize.X - 2 - _WindowSize.X % 2), short(_WindowSize.Y - 2 - _WindowSize.Y % 2)};
    return 0;
}
Action_t ScreenBuffer_t::GetWindowSize()
{
    CONSOLE_SCREEN_BUFFER_INFO csbi;

    GetConsoleScreenBufferInfo(GetStdHandle(STD_OUTPUT_HANDLE), &csbi);
    this->_WindowSize.Y = csbi.srWindow.Bottom - csbi.srWindow.Top + 1;
    this->_WindowSize.X = csbi.srWindow.Right - csbi.srWindow.Left + 1;
    if (_WindowSize.Y <= 0 || _WindowSize.X <= 0) GetWindowSize();
    return 0;
}

char*& ScreenBuffer_t::getbuffer()
{
    return this->storage;
}

Action_t ScreenBuffer_t::Fill(char c)
{
    const int S{_WindowSize.sqaure()};
    // size_t wchar_size = sizeof(char);
    // size_t bytes_to_fill = S * wchar_size;
    if (!storage) return -1;
    // method 1: basic for cycle fill
    // for (int i = 0; i < S; i++)
    //    storage[i] = c;

    // method 2: C type way to fill memory
    // memset(storage, c, sizeof(char) * S);

    // method 3: Cpp type way to fill memory
    std::fill(storage, storage + S, c);

    storage[S] = '\0';

    return 0;
}

Action_t ScreenBuffer_t::Fill(char* begin, char* end, char value)
{
    // [method 1]: basic for cycle fill
    // for (char *it = begin; it < end; it++) *it = value;

    // [method 2]: C type way to fill memory
    memset(begin, value, sizeof(char) * (end - begin));

    // [method 3]: Cpp type way to fill memory
    // std::fill(begin, end, value);
    return 0;
}

char& ScreenBuffer_t::operator[](int i)
{
    return this->storage[i];
}

#else
int ScreenBuffer::GetWindowSize()
{
    struct winsize w;
    ioctl(STDOUT_FILENO, TIOCGWINSZ, &w);
    if (ioctl(STDOUT_FILENO, TIOCGWINSZ, &w) == -1)
    {
        perror("ioctl TIOCGWINSZ");
        return 1;
    }
    this->_WindowSize.Y = size.ws_row;
    this->_WindowSize.X = size.ws_col;
    return 0;
}

#endif

void hide_cursor()
{
    printf("\033[?25l");
}
void show_cursor()
{
    printf("\033[?25h");
}

void save_cursor_position()
{
    printf("\033[s");
}

void restore_cursor_position()
{
    printf("\033[u");
}

void clear_screen()
{
    printf("\033[2J\033[H");
}

void ScreenBuffer_t::Display(ScreenBuffer_t* ptr_scrbuf)
{
    // hide_cursor();
    //  save_cursor_position();
    // clear_screen();
    printf("\033[1;1H");
    fwrite(ptr_scrbuf->getbuffer(), sizeof(char), ptr_scrbuf->_WindowSize.sqaure(), stdout);
    // LOG::ShowAll();
    printf("\033[1;1H");
    //  fflush(stdout);
    //   for (int y = 0; y < _WindowSize.Y; y++)
    //   {
    //
    //   }
    //   wprintf(L"%s", ptr_scrbuf->getbuffer());
    //   restore_cursor_position();
}
